package com.atguigu.gmall.order.biz.impl;

import com.atguigu.gmall.cart.entity.CartInfo;
import com.atguigu.gmall.cart.entity.UserAuth;
import com.atguigu.gmall.common.constant.RabbitConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.common.interceptor.component.UserAuthComponent;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.JSONs;
import com.atguigu.gmall.common.util.MathUtil;
import com.atguigu.gmall.enums.OrderStatus;
import com.atguigu.gmall.enums.PaymentWay;
import com.atguigu.gmall.enums.ProcessStatus;
import com.atguigu.gmall.feign.cart.CartFeignClient;
import com.atguigu.gmall.feign.product.SkuDetailItemFeignClient;
import com.atguigu.gmall.feign.user.UserFeignClient;
import com.atguigu.gmall.feign.ware.WareFeignClient;
import com.atguigu.gmall.mq.config.component.RabbitService;
import com.atguigu.gmall.mq.order.OrderCreateMsg;
import com.atguigu.gmall.mq.ware.WareDeductMsg;
import com.atguigu.gmall.mq.ware.WareStockMsg;
import com.atguigu.gmall.order.biz.OrderBizService;
import com.atguigu.gmall.order.entity.OrderDetail;
import com.atguigu.gmall.order.entity.OrderInfo;
import com.atguigu.gmall.order.entity.OrderStatusLog;
import com.atguigu.gmall.order.entity.PaymentInfo;
import com.atguigu.gmall.order.service.OrderDetailService;
import com.atguigu.gmall.order.service.OrderInfoService;
import com.atguigu.gmall.order.service.OrderStatusLogService;
import com.atguigu.gmall.order.service.PaymentInfoService;
import com.atguigu.gmall.order.vo.OrderConfirmVo;
import com.atguigu.gmall.order.vo.OrderSplitChildVo;
import com.atguigu.gmall.order.vo.OrderSplitVo;
import com.atguigu.gmall.order.vo.OrderSubmitVo;
import com.atguigu.gmall.user.entity.UserAddress;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @Author:lyd
 * @Date:2023/9/4 18:33
 * @Version:1.0
 * @Description
 **/
@Slf4j
@Service
public class OrderBizServiceImpl implements OrderBizService {

    @Autowired
    CartFeignClient cartFeignClient;
    @Autowired
    SkuDetailItemFeignClient skuFeignClient;
    @Autowired
    UserFeignClient userFeignClient;
    @Autowired
    UserAuthComponent userAuthComponent;

    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    WareFeignClient wareFeignClient;

    @Autowired
    ThreadPoolExecutor executor;

    @Autowired
    OrderInfoService orderInfoService;

    @Autowired
    OrderDetailService orderDetailService;

    @Autowired
    OrderStatusLogService logService;

    @Autowired
    PaymentInfoService paymentInfoService;

    @Autowired
    RabbitService rabbitService;

    /**
     * 获取订单确认页数据,没有加上异步处理
     *
     * @return
     */

    public OrderConfirmVo getOrderConfirmDataAA() {
        OrderConfirmVo confirmVo = new OrderConfirmVo();

        //共享老请求，即cookie和保存用户信息的token

        //1、获取商品清单的数据
        List<CartInfo> checkedList = cartFeignClient.getChecked().getData();
        List<OrderConfirmVo.Detail> detailList = checkedList
                .stream()
                .map(item -> {
                    OrderConfirmVo.Detail detail = new OrderConfirmVo.Detail();
                    detail.setSkuId(item.getSkuId());
                    detail.setImgUrl(item.getImgUrl());
                    detail.setSkuName(item.getSkuName());
                    //这个价格是商品的实时价格，需要再从数据库里面查询
                    BigDecimal price = skuFeignClient.getPrice(item.getSkuId()).getData();
                    detail.setOrderPrice(price);
                    detail.setSkuNum(item.getSkuNum());
                    //远程调用库存系统，判断是否还有库存
                    String hasStock = wareFeignClient.hasStock(item.getSkuId(), item.getSkuNum());
                    detail.setHasStock(Integer.parseInt(hasStock));
                    return detail;
                }).collect(Collectors.toList());
        confirmVo.setDetailArrayList(detailList);


        Integer totalNum = detailList
                .stream()
                .map(item -> item.getSkuNum())
                .reduce(0, (o1, o2) -> o1 + o2);//两两计算
        confirmVo.setTotalNum(totalNum);


        //3、获取商品的总价格
        BigDecimal totalAmount = detailList
                .stream()
                .map(item -> item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())))
                .reduce((o1, o2) -> o1.add(o2))
                .get();
        confirmVo.setTotalAmount(totalAmount);


        // 凡是以后要用到 token 的远程 异步调用，需要通过 ThreadLocal 重新共享老请求。

        //4、设置用户收获位置列表
        List<UserAddress> addressList = userFeignClient.getAddressList().getData();
        confirmVo.setUserAddressList(addressList);


        //5、设置用户订单交易编号
        //此交易号可用于防重处理，用令牌发来做防重处理
        //虽然order订单的交易号使用雪花算法生成，没有实际代表意义，仅用于用作索引
        //但我们对外暴露的tradeNo是具有实际意义的，有时间戳和用户id
        // atguigu_时间戳_4

        String token = "atguigu_" + System.currentTimeMillis() + "_" + userAuthComponent.getUserAuth().getUserId();
        confirmVo.setTradeNo(token);
        //给redis存一份
        redisTemplate.opsForValue().set(RedisConst.DE_REPEAT_TOKEN + token, "520", 10, TimeUnit.MINUTES);

        return confirmVo;
    }


    /**
     * 获取订单确认页数据,加上异步处理
     *
     * @return
     */
    @Override
    public OrderConfirmVo getOrderConfirmData() {
        OrderConfirmVo confirmVo = new OrderConfirmVo();
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        CompletableFuture<List<OrderConfirmVo.Detail>> detailFuture = CompletableFuture.supplyAsync(() -> {
            //共享老请求，即cookie和保存用户信息的token
            RequestContextHolder.setRequestAttributes(requestAttributes);
            //1、获取商品清单的数据
            List<CartInfo> checkedList = cartFeignClient.getChecked().getData();
            List<OrderConfirmVo.Detail> detailList = checkedList
                    .stream()
                    .parallel()//新线程
                    .map(item -> {
                        OrderConfirmVo.Detail detail = new OrderConfirmVo.Detail();
                        detail.setSkuId(item.getSkuId());
                        detail.setImgUrl(item.getImgUrl());
                        detail.setSkuName(item.getSkuName());
                        //这个价格是商品的实时价格，需要再从数据库里面查询
                        BigDecimal price = skuFeignClient.getPrice(item.getSkuId()).getData();
                        detail.setOrderPrice(price);
                        detail.setSkuNum(item.getSkuNum());
                        //远程调用库存系统，判断是否还有库存
                        String hasStock = wareFeignClient.hasStock(item.getSkuId(), item.getSkuNum());
                        detail.setHasStock(Integer.parseInt(hasStock));
                        return detail;
                    }).collect(Collectors.toList());
            confirmVo.setDetailArrayList(detailList);
            //移除 防止OOM
            RequestContextHolder.resetRequestAttributes();
            return detailList;
        }, executor);


        CompletableFuture<Void> totalNumFuture = detailFuture.thenAcceptAsync((result) -> {
            //2、获取其商品总的数量
            Integer totalNum = result
                    .stream()
                    .map(item -> item.getSkuNum())
                    .reduce(0, (o1, o2) -> o1 + o2);//两两计算
            confirmVo.setTotalNum(totalNum);
        }, executor);


        CompletableFuture<Void> totalPriceFuture = detailFuture.thenAcceptAsync((redult) -> {
            //3、获取商品的总价格
            BigDecimal totalAmount = redult
                    .stream()
                    .map(item -> item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())))
                    .reduce((o1, o2) -> o1.add(o2))
                    .get();
            confirmVo.setTotalAmount(totalAmount);
        }, executor);

        // 凡是以后要用到 token 的远程 异步调用，需要通过 ThreadLocal 重新共享老请求。
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
            // 异步线程的 ThreadLocal; 给异步绑定好老请求
            RequestContextHolder.setRequestAttributes(requestAttributes);

            //4、设置用户收获位置列表
            List<UserAddress> addressList = userFeignClient.getAddressList().getData();
            confirmVo.setUserAddressList(addressList);
            RequestContextHolder.resetRequestAttributes();
        }, executor);


        //5、设置用户订单交易编号
        //此交易号可用于防重处理，用令牌发来做防重处理
        //虽然order订单的交易号使用雪花算法生成，没有实际代表意义，仅用于用作索引
        //但我们对外暴露的tradeNo是具有实际意义的，有时间戳和用户id
        // atguigu_时间戳_4

        String token = "atguigu_" + System.currentTimeMillis() + "_" + userAuthComponent.getUserAuth().getUserId();
        confirmVo.setTradeNo(token);
        //给redis存一份
        redisTemplate.opsForValue().set(RedisConst.DE_REPEAT_TOKEN + token, "520", 10, TimeUnit.MINUTES);

        CompletableFuture.allOf(totalNumFuture, totalPriceFuture, addressFuture)
                .join();
        return confirmVo;
    }

    /**
     * 用户下单，分别保存数据到各自的表里面
     *
     * @param tradeNo
     * @param orderSubmitVo
     * @return 提交订单时需要做以下三部校验
     * 1、放重复提交校验
     * 2、商品库存校验
     * 3、商品价格校验
     */
    @Transactional
    @Override
    public Long submitOrder(String tradeNo, OrderSubmitVo orderSubmitVo) {
        //1、防重复提交校验,多次请求只处理一次
        Boolean delete = redisTemplate.delete(RedisConst.DE_REPEAT_TOKEN + tradeNo);
        if (!delete) {
            throw new GmallException(ResultCodeEnum.ORDER_MULTI_SUBMIT);
        }

        //2、商品库存校验,需要下单的所有商品都需要是有货状态。要保证下单成功，需要删除订单页面中没有库存的商品
        List<String> noStockName = orderSubmitVo.getOrderDetailList()
                .stream()
                .parallel()
                .filter(item -> {
                    Long skuId = item.getSkuId();
                    String hasStock = wareFeignClient.hasStock(skuId, item.getSkuNum());
                    return "0".equalsIgnoreCase(hasStock);
                })
                .map(item -> item.getSkuName())
                .collect(Collectors.toList());
        //没有库存商品则报错
        if (noStockName != null && noStockName.size() > 0) {
            ResultCodeEnum orderHasNoStock = ResultCodeEnum.ORDER_HAS_NO_STOCK;
            String jsonStr = JSONs.toJSONStr(noStockName);
            throw new GmallException(orderHasNoStock.getMessage() + jsonStr, orderHasNoStock.getCode());
        }


        //3、商品价格校验，保证商品的价格是数据库最新的价格
        List<String> priceChangeSku = orderSubmitVo.getOrderDetailList()
                .stream()
                .parallel()
                .filter(item -> {
                    BigDecimal orderPrice = item.getOrderPrice();
                    BigDecimal newPrice = skuFeignClient.getPrice(item.getSkuId()).getData();
                    return !MathUtil.decimalEquals(orderPrice, newPrice);
                })
                .map(item -> item.getSkuName())
                .collect(Collectors.toList());
        if (priceChangeSku != null && priceChangeSku.size() > 0) {
            ResultCodeEnum orderPriceChange = ResultCodeEnum.ORDER_PRICE_CHANGE;
            String jsonStr = JSONs.toJSONStr(noStockName);
            throw new GmallException(orderPriceChange.getMessage() + jsonStr, orderPriceChange.getCode());
        }


        //走到这一步，说明前端提交的数据是合法有效的
        //接下来，将保存前端的数据到数据库里面
        OrderInfo orderInfo = prepareOrderInfo(tradeNo, orderSubmitVo);

        //将订单基本信息保存到order_info
        orderInfoService.save(orderInfo);

        Long orderId = orderInfo.getId();
        //将订单明细信息保存到order_detail
        List<OrderDetail> orderDetails = orderSubmitVo.getOrderDetailList()
                .stream()
                .map(item -> {
                    OrderDetail orderDetail = new OrderDetail();
                    orderDetail.setUserId(orderInfo.getUserId());
                    orderDetail.setOrderId(orderId);
                    orderDetail.setSkuId(item.getSkuId());
                    orderDetail.setSkuName(item.getSkuName());
                    orderDetail.setImgUrl(item.getImgUrl());
                    orderDetail.setOrderPrice(item.getOrderPrice());
                    orderDetail.setSkuNum(item.getSkuNum());
                    orderDetail.setCreateTime(new Date());

                    //分割 实际支付金额； 当前这个商品的实际支付总价;
                    // 实际支付金额 = 当前商品原价 - 均摊优惠价；
                    // 退款。
                    orderDetail.setSplitTotalAmount(item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())));

                    //促销分摊金额
                    orderDetail.setSplitActivityAmount(new BigDecimal("0"));
                    //优惠卷分摊金额
                    orderDetail.setSplitCouponAmount(new BigDecimal("0"));
                    return orderDetail;
                }).collect(Collectors.toList());

        orderDetailService.saveBatch(orderDetails);


        //下单成功，记录订单状态流转日志
        OrderStatusLog log = new OrderStatusLog();
        log.setUserId(orderInfo.getUserId());
        log.setOrderId(orderId);
        log.setOrderStatus(orderInfo.getOrderStatus());
        log.setOperateTime(new Date());
        logService.save(log);

        //创建消息
        OrderCreateMsg msg = new OrderCreateMsg(orderId, orderInfo.getUserId(), new Date(), RabbitConst.RK_ORDER_CREATE);
        //发送消息
        rabbitService.sendMsg(RabbitConst.EXCHANGE_ORDER_EVENT, RabbitConst.RK_ORDER_CREATE, msg);

        return orderId;
    }


    /**
     * 关闭订单
     *
     * @param orderId
     * @param userId
     */
    @Transactional
    @Override
    public void closeOrder(Long orderId, Long userId) {
        // 如果订单还没有支付；把订单状态由 未支付 变为 已关闭；
        //update order_info set
        //	order_status=CLOSED, process_status=CLOSED
        //	where id=? and user_id=? and order_status=unpaid
        boolean update = orderInfoService.lambdaUpdate()
                .set(OrderInfo::getOrderStatus, OrderStatus.CLOSED.name())
                .set(OrderInfo::getProcessStatus, OrderStatus.CLOSED.name())
                .set(OrderInfo::getOperateTime, new Date())
                .eq(OrderInfo::getId, orderId)
                .eq(OrderInfo::getUserId, userId)
                .eq(OrderInfo::getOrderStatus, OrderStatus.UNPAID.name())
                .update();
//        int i=10/0;
        //时间到30分钟后，关闭订单
        //关闭订单时，会出现一种极端情况，就是在30分钟的临界点用户的刚好支付成功，此时需要对这种情况处理
        //1、关单快，支付慢。我们就需要在支付成功，支付宝异步返回通知时修改订单状态
        //2、支付快，关单满。 此时，订单的支付状态是已支付，就不不需要做任何处理
        if (update) {
            OrderStatusLog statusLog = new OrderStatusLog();
            statusLog.setUserId(userId);
            statusLog.setOrderId(orderId);
            statusLog.setOrderStatus(OrderStatus.CLOSED.name());
            statusLog.setOperateTime(new Date());
            logService.save(statusLog);
        } else {
            //代表 订单状态已经不是期望状态
            log.info("订单已被支付，无需关闭； 订单号：{} ，用户id：{}", orderId, userId);
        }
    }

    @Override
    public OrderInfo getOrderInfo(Long orderId) {

        Long userId = userAuthComponent.getUserAuth().getUserId();
        OrderInfo orderInfo = orderInfoService.lambdaQuery()
                .eq(OrderInfo::getId, orderId)
                .eq(OrderInfo::getUserId, userId)
                .one();
        return orderInfo;
    }


    @Override
    public void payOrderStatus(Map<String, String> msg) {
        String tradeNo = msg.get("out_trade_no");

        String[] split = tradeNo.split("_");
        long userId = Long.parseLong(split[split.length - 1]);

        //按照outTradeNo把订单信息查询出来
        OrderInfo info = orderInfoService.lambdaQuery()
                .eq(OrderInfo::getOutTradeNo, tradeNo)
                .eq(OrderInfo::getUserId, userId)
                .one();


        //1、订单 CAS变状态； 未支付或者已关闭都可以修改为 已支付
        boolean update = orderInfoService.lambdaUpdate()
                .set(OrderInfo::getOrderStatus, OrderStatus.PAID.name())
                .set(OrderInfo::getProcessStatus, ProcessStatus.PAID.name())
                .eq(OrderInfo::getId, info.getId())
                .eq(OrderInfo::getUserId, userId)
                .in(OrderInfo::getOrderStatus,
                        OrderStatus.UNPAID.name(),
                        OrderStatus.CLOSED.name())
                .update();


        if (update) {
            //2、记录订单状态日志
            OrderStatusLog statusLog = new OrderStatusLog();
            statusLog.setUserId(userId);
            statusLog.setOrderId(info.getId());
            statusLog.setOrderStatus(OrderStatus.PAID.name());
            statusLog.setOperateTime(new Date());
            logService.save(statusLog);

            //3、把此次支付信息保存到支付信息日志表
            PaymentInfo paymentInfo = new PaymentInfo();
            paymentInfo.setUserId(userId);
            paymentInfo.setOutTradeNo(tradeNo);
            paymentInfo.setOrderId(info.getId());
            paymentInfo.setPaymentType("支付宝");
            //支付流水号。第三方支付系统会给我们返回
            paymentInfo.setTradeNo(msg.get("trade_no"));
            paymentInfo.setTotalAmount(new BigDecimal(msg.get("total_amount")));
            paymentInfo.setSubject(msg.get("subject"));

            //支付状态： trade_status
            paymentInfo.setPaymentStatus(msg.get("trade_status"));
            paymentInfo.setCreateTime(new Date());
            paymentInfo.setCallbackTime(new Date());
            //回调内容
            paymentInfo.setCallbackContent(JSONs.toJSONStr(msg));
            paymentInfoService.save(paymentInfo);

            //给库存系统发送扣减这个订单库存的消息;  库存系统没有幂等性。
            WareStockMsg wareStockMsg = prepareWareStockMsg(info);
            rabbitService.sendMsg(RabbitConst.EXCHANGE_WARE_STOCK, RabbitConst.RK_WARE_STOCK, wareStockMsg);

        }


    }

    @Override
    public List<OrderSplitChildVo> splitOrder(OrderSplitVo orderSplitVo) {


        //1、父订单id
        Long parentOrderId = orderSplitVo.getOrderId();
        //2、得到原来父订单的所有数据
        OrderInfo parentOrder = orderInfoService.getById(parentOrderId);
        //3、查询父订单原来的所有明细信息；
        List<OrderDetail> details = orderDetailService.lambdaQuery()
                .eq(OrderDetail::getUserId, parentOrder.getUserId())
                .eq(OrderDetail::getOrderId, parentOrderId)
                .list();

        //4、仓库编号与商品的对照关系
        List<OrderSplitVo.WareSkuMap> wareSkuMaps = JSONs.jsonToObj(orderSplitVo.getWareSkuMap(), new TypeReference<List<OrderSplitVo.WareSkuMap>>() {
        });


        //拆单实现
        AtomicInteger i = new AtomicInteger();

        if (parentOrder.getOrderStatus().equals(OrderStatus.SPLIT.name())) {
            throw new GmallException(ResultCodeEnum.ORDER_HAS_SPLIT);
        }
        //构造并保存子订单的信息和明细数据
        List<OrderSplitChildVo> childVos = wareSkuMaps.stream()
                .map(skuMap -> buildChildOrder(parentOrder, details, skuMap, i.getAndIncrement()))
                .collect(Collectors.toList());

        //修改订单状态
        boolean update = orderInfoService.lambdaUpdate()
                .set(OrderInfo::getOrderStatus, OrderStatus.SPLIT)
                .set(OrderInfo::getOrderStatus, ProcessStatus.SPLIT)
                .eq(OrderInfo::getId, parentOrderId)
                .eq(OrderInfo::getUserId, parentOrder.getUserId())
                .eq(OrderInfo::getOrderStatus, OrderStatus.PAID.name())
                .update();
        if (update) {
            OrderStatusLog statusLog = new OrderStatusLog();
            statusLog.setUserId(parentOrder.getUserId());
            statusLog.setOrderId(parentOrderId);
            statusLog.setOrderStatus(OrderStatus.SPLIT.name());
            statusLog.setOperateTime(new Date());
            logService.save(statusLog);
        }
        return childVos;
    }

    private OrderSplitChildVo buildChildOrder(OrderInfo parentOrder,
                                              List<OrderDetail> details,
                                              OrderSplitVo.WareSkuMap skuMap,
                                              int i) {
        //获取当前仓库下有哪些商品
        List<Long> skuIdList = skuMap.getSkuIds();
        //通过不同仓库下存放的货物，获取子单的详细数据
        List<OrderDetail> childOrderDetails = details.stream()
                .filter(item -> {
                    Long skuId = item.getSkuId();
                    return skuIdList.contains(skuId);
                }).collect(Collectors.toList());

        //获取子单
        OrderInfo childOrderInfo = new OrderInfo();
        childOrderInfo.setConsignee(parentOrder.getConsignee());
        childOrderInfo.setConsigneeTel(parentOrder.getConsigneeTel());
        BigDecimal totalAmount = childOrderDetails.stream()
                .map(item -> item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())))
                .reduce((o1, o2) -> o1.add(o2))
                .get();
        childOrderInfo.setTotalAmount(totalAmount);
        //这些内容和父订单保持一致
        childOrderInfo.setOrderStatus(parentOrder.getOrderStatus());
        childOrderInfo.setUserId(parentOrder.getUserId());
        childOrderInfo.setPaymentWay("2");
        childOrderInfo.setDeliveryAddress(parentOrder.getDeliveryAddress());
        childOrderInfo.setOrderComment(parentOrder.getOrderComment());

        //对外交易号
        childOrderInfo.setOutTradeNo("child" + i + ":" + parentOrder.getOutTradeNo());

        childOrderInfo.setTradeBody(parentOrder.getTradeBody());
        childOrderInfo.setCreateTime(new Date());
        childOrderInfo.setExpireTime(parentOrder.getExpireTime());
        childOrderInfo.setProcessStatus(parentOrder.getProcessStatus());

        //物流号等库存扣减完成并出库对接物流系统就会有
        childOrderInfo.setTrackingNo("");

        childOrderInfo.setParentOrderId(parentOrder.getId());
        childOrderInfo.setImgUrl(childOrderDetails.get(0).getImgUrl());
        childOrderInfo.setProvinceId(parentOrder.getProvinceId());
        childOrderInfo.setOperateTime(new Date());
        childOrderInfo.setActivityReduceAmount(new BigDecimal("0"));
        childOrderInfo.setCouponAmount(new BigDecimal("0"));
        childOrderInfo.setOriginalTotalAmount(new BigDecimal("0"));
        childOrderInfo.setFeightFee(new BigDecimal("0"));
//        childOrderInfo.setRefundableTime();

        //保存子订单
        orderInfoService.save(childOrderInfo);
        //子订单id
        Long childOrderId = childOrderInfo.getId();


        //保存子订单购买的所有商品
        List<OrderDetail> childDetails = childOrderDetails.stream()
                .map(item -> {
                    item.setId(null);
                    item.setOrderId(childOrderId);
                    return item;
                }).collect(Collectors.toList());
        orderDetailService.saveBatch(childDetails);

        //封装返回拆单后给库存系统的数据
        OrderSplitChildVo splitChildVo = new OrderSplitChildVo();
        splitChildVo.setOrderBody(childOrderInfo.getTradeBody());
        splitChildVo.setConsignee(childOrderInfo.getConsignee());
        splitChildVo.setConsigneeTel(childOrderInfo.getConsigneeTel());
        splitChildVo.setOrderComment(childOrderInfo.getOrderComment());
        splitChildVo.setWareId(skuMap.getWareId());
        splitChildVo.setOrderId(childOrderId);
        splitChildVo.setDeliveryAddress(childOrderInfo.getDeliveryAddress());
        splitChildVo.setPaymentWay(childOrderInfo.getPaymentWay());

        List<OrderSplitChildVo.DetailsDTO> childDetailsDto = childDetails.stream()
                .map(item -> {
                    OrderSplitChildVo.DetailsDTO detailsDTO = new OrderSplitChildVo.DetailsDTO();
                    detailsDTO.setSkuName(item.getSkuName());
                    detailsDTO.setSkuId(item.getSkuId());
                    detailsDTO.setSkuNum(item.getSkuNum());
                    return detailsDTO;
                }).collect(Collectors.toList());

        splitChildVo.setDetails(childDetailsDto);

        return splitChildVo;
    }

    @Override
    public void deductStatusOrder(WareDeductMsg msg) {

        Long orderId = msg.getOrderId();
        String status = msg.getStatus();

        OrderStatus os = null;
        ProcessStatus ps = null;
        //状态： ‘DEDUCTED’  (已减库存)
        //状态：  ‘OUT_OF_STOCK’  (库存超卖)
        switch (status) {
            case "DEDUCTED":
                os = OrderStatus.WAITING_DELEVER;
                ps = ProcessStatus.WAITING_DELEVER;
                break;
            case "OUT_OF_STOCK":
                os = OrderStatus.WAITING_SCHEDULE;
                ps = ProcessStatus.STOCK_EXCEPTION;
                break;
        }

        OrderInfo info = orderInfoService.getById(orderId);

        //1、修改订单状态； CAS思想保证幂等
        boolean update = orderInfoService.lambdaUpdate()
                .set(OrderInfo::getOrderStatus, os.name())
                .set(OrderInfo::getProcessStatus, ps.name())
                .eq(OrderInfo::getId, orderId)
                .eq(OrderInfo::getUserId, info.getUserId())
                .eq(OrderInfo::getOrderStatus, OrderStatus.PAID.name())
                .update();

        OrderInfo infoNew = orderInfoService.getById(orderId);
        if (update) {
            //2、记录订单状态变化
            OrderStatusLog orderStatusLog = new OrderStatusLog();
            orderStatusLog.setUserId(info.getUserId());
            orderStatusLog.setOrderId(info.getId());
            orderStatusLog.setOrderStatus(infoNew.getOrderStatus());
            orderStatusLog.setOperateTime(new Date());
            logService.save(orderStatusLog);
            //TODO 对接物流系统，让物流系统为这个出库的单准备物流单号。
        }
        //TODO 3、库存预警消息发送【发短信、发邮件、对接供货商】; 提示商品需要调货部署
        if (status.equals("OUT_OF_STOCK")) {
            //1、查询订单购买了哪些商品，几件。
            log.info("库存超卖，正在通知供货商进行补货...");
        }


    }

    /**
     * 把用户提交的数据封装到OrderInfo里面
     *
     * @param tradeNo
     * @param orderSubmitVo
     * @return
     */
    private OrderInfo prepareOrderInfo(String tradeNo, OrderSubmitVo orderSubmitVo) {
        OrderInfo info = new OrderInfo();
        //收货人
        info.setConsignee(orderSubmitVo.getConsignee());
        //收货人电话
        info.setConsigneeTel(orderSubmitVo.getConsigneeTel());
        //商品总价格,订单需要支付的总金额
        BigDecimal totalAmount = orderSubmitVo.getOrderDetailList()
                .stream()
                .map(item -> item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())))
                .reduce((o1, o2) -> o1.add(o2))
                .get();
        info.setTotalAmount(totalAmount);

        //订单的状态
        info.setOrderStatus(OrderStatus.UNPAID.name());

        //用户id
        UserAuth userAuth = userAuthComponent.getUserAuth();
        Long userId = userAuth.getUserId();
        info.setUserId(userId);

        //支付方式
        info.setPaymentWay(PaymentWay.ONLINE.name());

        //送货地址
        String deliveryAddress = orderSubmitVo.getDeliveryAddress();
        info.setDeliveryAddress(deliveryAddress);

        //订单留言
        info.setOrderComment(orderSubmitVo.getOrderComment());

        //对外交易号
        info.setOutTradeNo(tradeNo);
        //交易体
        info.setTradeBody(orderSubmitVo.getOrderDetailList().get(0).getSkuName());

        //创建时间
        info.setCreateTime(new Date());

        //订单未支付的失效时间
        info.setExpireTime(new Date(System.currentTimeMillis() + 1000 * 60 * 30L));

        //给后台系统看的订单状态
        info.setProcessStatus(ProcessStatus.UNPAID.name());

        //出库以后，库存系统对接物流提供物流单号，此时再改数据库的此订单物流单号即可
        info.setTrackingNo("");

        //拆单
        info.setParentOrderId(0L);

        //订单图片概览信息
        info.setImgUrl(orderSubmitVo.getOrderDetailList().get(0).getImgUrl());

        //省id； 没用
        info.setProvinceId(0L);

        //订单最后一次发生修改的时间。
        info.setOperateTime(new Date());

        //促销金额
        info.setActivityReduceAmount(new BigDecimal("0"));

        //优惠券金额
        info.setCouponAmount(new BigDecimal("0"));

        //原价金额： 促销优惠券没有减之前的金额；
        info.setOriginalTotalAmount(totalAmount);

        //运费
        info.setFeightFee(new BigDecimal("0"));
        return info;
    }

    /**
     * 封装减库存的消息
     *
     * @param info
     * @return
     */
    private WareStockMsg prepareWareStockMsg(OrderInfo info) {
        WareStockMsg wareStockMsg = new WareStockMsg();
        wareStockMsg.setOrderId(info.getId());
        wareStockMsg.setConsignee(info.getConsignee());
        wareStockMsg.setConsigneeTel(info.getConsigneeTel());
        wareStockMsg.setOrderComment(info.getOrderComment());
        wareStockMsg.setOrderBody(info.getTradeBody());
        wareStockMsg.setDeliveryAddress(info.getDeliveryAddress());
        wareStockMsg.setPaymentWay("2");//‘1’ 为货到付款，‘2’为在线支付。

        //商品的明细
        List<OrderDetail> details = orderDetailService.lambdaQuery()
                .eq(OrderDetail::getOrderId, info.getId())
                .eq(OrderDetail::getUserId, info.getUserId())
                .list();

        List<WareStockMsg.Detail> collect = details.stream()
                .map(item -> {
                    WareStockMsg.Detail detail = new WareStockMsg.Detail();
                    detail.setSkuId(item.getSkuId());
                    detail.setSkuNum(item.getSkuNum());
                    detail.setSkuName(item.getSkuName());
                    return detail;
                }).collect(Collectors.toList());
        wareStockMsg.setDetails(collect);

        return wareStockMsg;
    }
}